/*
 * SdlSocketViewerProtocol.cpp
 *
 *  Created on: May 25, 2011
 *      Author: madboyka
 */

#include "SdlSocketViewerProtocol.h"
#include <netdb.h>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include "Logger.h"

#define MAXPENDING 5    /* Max connection requests */

void SdlSocketViewerProtocol::connect(QString address)
{
	if (SDLNet_ResolveHost(&ip, address.toLocal8Bit().data(), PORT) < 0)
	{
		throw NetworkException(SDLNet_GetError());
	}
	if (!(socket = SDLNet_TCP_Open(&ip)))
	{
		throw NetworkException(SDLNet_GetError());
	}
}

float* SdlSocketViewerProtocol::getLocationData()
{
	return ldata;
}

int SdlSocketViewerProtocol::getCount()
{
	return count;
}

SdlSocketViewerProtocol::SdlSocketViewerProtocol(TCPsocket clientSocket)
{
	socket = clientSocket;
}

SdlSocketViewerProtocol::SdlSocketViewerProtocol()
{
	if (SDLNet_Init() < 0)
	{
		throw NetworkException("SDLNet_init failed.");
	}
	ldata = 0;
}

SdlSocketViewerProtocol::~SdlSocketViewerProtocol()
{
	SDLNet_Quit();
}

int SdlSocketViewerProtocol::receive()
{
#ifdef TEMP
	float *temp = ldata;
#endif
	//delete []ldata;

	int messageType;
	if (SDLNet_TCP_Recv(socket, &messageType, sizeof(int)) <= 0)
	{
		throw NetworkException("Receive error...");
	}
	if (SDLNet_TCP_Recv(socket, &count, sizeof(int)) <= 0)
	{
		throw NetworkException("Error on connect");
	}

	//	printf("count: %d", count);
	switch (messageType)
	{
	case ADD_ELEMENTS:
		newElements.clear();
		for (int i = 0; i < count; ++i)
		{
			ElementData temp = readElementData();
//			printf("recieve: %d\n", temp.getDataCount());
			newElements.append(temp);
//			puts("Got new element data!");
		}
		break;
	case DELETE_ELEMENTS:

		break;
	case CHANGE_ELEMENTS:
		ldata = new float[count * 16];
		for (int i = 0; i < count; ++i)
		{
			while (SDLNet_TCP_Recv(socket, ldata + (16 * i), sizeof(float) * 16)
					<= 0)
			{
				printf("receive: Sex van!\n");
				//				throw NetworkException("Error on connect");
			}
		}
		break;
	}
//	puts("end of SdlSocketViewerProtocol::receive()");
#ifdef TEMP
	delete []temp;
#endif
	return messageType;
}

void SdlSocketViewerProtocol::bind()
{
//	puts("binding address");
	if (SDLNet_ResolveHost(&ip, NULL, PORT) < 0)
	{
		throw NetworkException(SDLNet_GetError());
	}

	if (!(socket = SDLNet_TCP_Open(&ip)))
	{
		throw NetworkException(SDLNet_GetError());
	}
}

ViewerProtocol *SdlSocketViewerProtocol::acceptConnection()
{
//	puts("Going to accept connection.");
	TCPsocket clientSocket;
	IPaddress* remoteIP;
	while (!(clientSocket = SDLNet_TCP_Accept(socket)))
		usleep(10);
	if ((remoteIP = SDLNet_TCP_GetPeerAddress(socket)))
		printf("Host connected: %x %d\n", SDLNet_Read32(&remoteIP->host),
				SDLNet_Read16(&remoteIP->port));
	else
		fprintf(stderr, "SDLNet_TCP_GetPeerAddress: %s\n", SDLNet_GetError());
	return new SdlSocketViewerProtocol(clientSocket);
}

void SdlSocketViewerProtocol::sendMessage(int type, int count)
{
	if (SDLNet_TCP_Send(socket, (void*) &type, sizeof(int)) <= 0)
	{
		puts("Failed to SDLNet_TCP_Send bytes to client");
	}
	if (SDLNet_TCP_Send(socket, (void*) &count, sizeof(int)) <= 0)
	{
		puts("Failed to SDLNet_TCP_Send bytes to client");
	}

}

void SdlSocketViewerProtocol::sendObjects(const void *data, int size)
{
	if (SDLNet_TCP_Send(socket, (void*) data, size) <= 0)
	{
		throw NetworkException(QString(
				"Failed to SDLNet_TCP_Send bytes to client"));
	}
}

void SdlSocketViewerProtocol::close()
{
	puts("Trying to close viewer!");
	SDLNet_TCP_Close(socket);
}

void SdlSocketViewerProtocol::waitRequest()
{
//	puts("SdlSocketViewerProtocol::waitRequest()");
	int request;
	if (SDLNet_TCP_Recv(socket, &request, sizeof(int)) <= 0)
	{
		throw NetworkException(QString("Failed to wait for request!")
				+ QString(SDLNet_GetError()));
	}
//	printf("Got request: %d \n", request);
}

void SdlSocketViewerProtocol::sendRequest()
{
	int request = 1;
	if (SDLNet_TCP_Send(socket, &request, sizeof(int)) <= 0)
	{
		throw NetworkException("Request error");
	}
}

void SdlSocketViewerProtocol::writeElementData(const ElementData edata)
{
//	Logger::log("Writing new Element data...");
	ElementType etype = edata.getElementType();
	sendObjects(&etype, sizeof(ElementType));
	sendObjects(edata.getColor(), sizeof(unsigned char) * 3);
	Logger::log("Colors sent: %u %u %u", edata.getColor()[0],edata.getColor()[1],edata.getColor()[2]);
	sendObjects(edata.getTransformationMatrix(), sizeof(float) * 16);
	//	int *datacount = new int;
	//	*datacount = edata.dataCount;
	if (etype != MeshType)
	{
		int x = edata.getDataCount();
//		Logger::log("datacount is %d", x);
		sendObjects(&x, sizeof(int));
		sendObjects(edata.getData(), sizeof(float) * x);
	}
	else
	{
//		Logger::log("Sending meshtype: %s", edata.getMeshType());
		int x = strlen(edata.getMeshType()) + 1;
		sendObjects(&x, sizeof(int));
		sendObjects(edata.getMeshType(), sizeof(char)*x);
	}
}

ElementData SdlSocketViewerProtocol::readElementData()
{
	ElementType etype;
//	Logger::log("Reading new element data...");
	if (SDLNet_TCP_Recv(socket, &etype, sizeof(ElementType)) <= 0)
	{
		throw NetworkException(QString("Failed to wait for request!"));
	}
	ElementData edata(etype);
	unsigned char *colors = new unsigned char[3];
	if (SDLNet_TCP_Recv(socket, colors, sizeof(unsigned char) * 3) <= 0)
	{
		delete[] colors;
		throw NetworkException(QString("Failed to wait for request!"));
	}
	edata.setColor(colors[0], colors[1], colors[2]);
	Logger::log("Got colors: %u %u %u", colors[0],colors[1],colors[2]);
	delete[] colors;
	float* tm = new float[16];
	if (SDLNet_TCP_Recv(socket, tm, sizeof(float) * 16) <= 0)
	{
		throw NetworkException(QString("Failed to wait for request!"));
	}
	edata.setTransformationMatrix(tm);
	delete[] tm;
	if (etype != MeshType)
	{
		int dataCount;
		if (SDLNet_TCP_Recv(socket, &dataCount, sizeof(int)) <= 0)
		{
			throw NetworkException(QString("Failed to wait for request!"));
		}
//		Logger::log("Received dataCount: %d", dataCount);
		tm = new float[dataCount];
		for (int i = 0; i < dataCount; ++i)
		{
			if (SDLNet_TCP_Recv(socket, tm + i, sizeof(float)) <= 0)
			{
				throw NetworkException(QString("Failed to wait for request!"));
			}
		}
		edata.setData(dataCount, tm);
		delete [] tm;
	}
	else
	{
		int nameLength;
		if (SDLNet_TCP_Recv(socket, &nameLength, sizeof(int)) <= 0)
		{
			throw NetworkException(QString("Failed to wait for request!"));
		}
//		Logger::log("MeshType of length: %d", nameLength);
		char* meshType = new char[nameLength];
		if (SDLNet_TCP_Recv(socket, meshType, sizeof(char)*nameLength) <= 0)
		{
			throw NetworkException(QString("Failed to wait for request!"));
		}
//		Logger::log("Got MeshType: %s", meshType);
		edata.setMeshType(meshType);
		delete [] meshType;

	}
	return edata;
}

ElementData SdlSocketViewerProtocol::getNewElement(int index)
{
	ElementData temp(newElements[index]);
	return temp;
}
